package com.mlethe.library.calendarview;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.widget.AbsListView;
import android.widget.LinearLayout;

import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;

import java.util.ArrayList;
import java.util.List;

/**
 * 日历布局
 */
public class CalendarLayout extends LinearLayout {

    /**
     * 多点触控支持
     */
    private int mActivePointerId;

    private static final int ACTIVE_POINTER = 1;

    private static final int INVALID_POINTER = -1;

    /**
     * 日历视图
     */
    CalendarView mCalendarView;

    /**
     * 第一个view
     */
    ViewGroup mTopView;

    /**
     * ContentView
     */
    ViewGroup mContentView;

    /**
     * 默认手势
     */
    private static final int GESTURE_MODE_NORMAL = 0;

    /**
     * 自动收起
     */
    private static final int GESTURE_MODE_RETRACT = 1;

    /**
     * 禁用手势
     */
    private static final int GESTURE_MODE_DISABLED = 2;

    /**
     * 手势模式
     */
    private final int mGestureMode;

    /**
     * ContentView  可滑动的最大距离距离 , 固定
     */
    private int mContentViewTranslateY;

    /**
     * 滑动吸顶距离
     */
    private int mCeilingHeight;

    private float downY;
    private float mLastY;
    private float mLastX;
    /**
     * 是否可以横向滑动
     */
    private boolean mLateralSlidingEnable;
    private boolean isAnimating = false;

    /**
     * 日历布局id
     */
    private final int mCalendarViewId;

    /**
     * 内容布局id
     */
    private final int mContentViewId;

    /**
     * 手速判断
     */
    private final VelocityTracker mVelocityTracker;
    private final int mMaximumVelocity;

    /**
     * 是否支持横向滑动，默认 false 不支持
     */
    private boolean mHorizontalScrollEnable;

    /**
     * 支持横向滑动的view的列表
     */
    private final List<View> mHorizontalScrollList = new ArrayList<>();

    public CalendarLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        setOrientation(LinearLayout.VERTICAL);
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CalendarLayout);
        mCalendarViewId = array.getResourceId(R.styleable.CalendarLayout_calendarViewId, 0);
        mContentViewId = array.getResourceId(R.styleable.CalendarLayout_scrollViewId, 0);
        mGestureMode = array.getInt(R.styleable.CalendarLayout_gestureMode, GESTURE_MODE_NORMAL);
        mCeilingHeight = array.getDimensionPixelSize(R.styleable.CalendarLayout_ceilingHeight, 0);
        mHorizontalScrollEnable = array.getBoolean(R.styleable.CalendarLayout_horizontalScrollEnable, false);
        array.recycle();
        mVelocityTracker = VelocityTracker.obtain();
        final ViewConfiguration configuration = ViewConfiguration.get(context);
//        int mTouchSlop = configuration.getScaledTouchSlop();
        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
    }

    /**
     * View 移动
     *
     * @param oldContentViewTranslateY 之前的移动距离
     */
    private void viewTranslateY(int oldContentViewTranslateY) {
        int contentViewTranslateY = getContentViewTranslateY();
        int diff = contentViewTranslateY - oldContentViewTranslateY;
        if (diff < 0 && mTopView.getTranslationY() < -contentViewTranslateY) {
            mTopView.setTranslationY(-contentViewTranslateY);
            mContentView.setTranslationY(-contentViewTranslateY);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (mGestureMode == GESTURE_MODE_DISABLED) {//禁用手势，或者只显示某种视图
            return false;
        }
        if (mContentView == null || mTopView == null || mTopView.getVisibility() == GONE) {
            return false;
        }

        int action = event.getAction();
        float y = event.getY();
        mVelocityTracker.addMovement(event);
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                int index = event.getActionIndex();
                mActivePointerId = event.getPointerId(index);
                mLastY = downY = y;
                return true;
            case MotionEvent.ACTION_POINTER_DOWN: {
                final int indexx = event.getActionIndex();
                mActivePointerId = event.getPointerId(indexx);
                if (mActivePointerId == 0) {
                    //核心代码：就是让下面的 dy = y- mLastY == 0，避免抖动
                    mLastY = event.getY(mActivePointerId);
                }
                break;
            }
            case MotionEvent.ACTION_MOVE:

                getPointerIndex(event, mActivePointerId);
                if (mActivePointerId == INVALID_POINTER) {
                    //如果切换了手指，那把mLastY换到最新手指的y坐标即可，核心就是让下面的 dy== 0，避免抖动
                    mLastY = y;
                    mActivePointerId = ACTIVE_POINTER;
                }
                float dy = y - mLastY;

                int contentViewTranslateY = getContentViewTranslateY();

                //向上滑动，并且contentView平移到最大距离，显示周视图
                float translationY = mContentView.getTranslationY();
                if (dy < 0 && translationY == -contentViewTranslateY) {
                    mLastY = y;
                    event.setAction(MotionEvent.ACTION_DOWN);
                    dispatchTouchEvent(event);
                    if (translationY == -mContentViewTranslateY) {
                        mTopView.setVisibility(INVISIBLE);
                    }
                    return true;
                }
                mTopView.setVisibility(VISIBLE);

                //向下滑动，并且contentView已经完全平移到底部
                if (dy > 0 && translationY + dy >= 0) {
                    mContentView.setTranslationY(0);
                    translationViewPager();
                    mLastY = y;
                    return super.onTouchEvent(event);
                }

                //向上滑动，并且contentView已经平移到最大距离，则contentView平移到最大的距离
                if (dy < 0 && translationY + dy <= -contentViewTranslateY) {
                    mContentView.setTranslationY(-contentViewTranslateY);
                    translationViewPager();
                    mLastY = y;
                    return super.onTouchEvent(event);
                }
                //否则按比例平移
                mContentView.setTranslationY(translationY + dy);
                translationViewPager();
                mLastY = y;
                break;
            case MotionEvent.ACTION_CANCEL:

            case MotionEvent.ACTION_POINTER_UP:
                int pointerIndex = getPointerIndex(event, mActivePointerId);
                if (mActivePointerId == INVALID_POINTER)
                    break;
                mLastY = event.getY(pointerIndex);
                break;
            case MotionEvent.ACTION_UP:

                final VelocityTracker velocityTracker = mVelocityTracker;
                velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
                float mYVelocity = velocityTracker.getYVelocity();
                if (mGestureMode == GESTURE_MODE_NORMAL) {
                    float abs = Math.abs(mYVelocity);
                    if (abs >= 10) {
                        int scrollHeight = (int) (abs / 10);
                        if (mYVelocity < 0) {
                            scrollShrink(scrollHeight);
                        } else {
                            scrollExpand(scrollHeight);
                        }
                    }
                    return super.onTouchEvent(event);
                } else if (mGestureMode == GESTURE_MODE_RETRACT) {
                    if (mContentView.getTranslationY() == 0
                            || mContentView.getTranslationY() == getContentViewTranslateY()) {
                        expand();
                        break;
                    }
                    if (Math.abs(mYVelocity) >= 800) {
                        if (mYVelocity < 0) {
                            shrink();
                        } else {
                            expand();
                        }
                        return super.onTouchEvent(event);
                    }
                    if (event.getY() - downY > 0) {
                        expand();
                    } else {
                        shrink();
                    }
                }
                break;
        }
        return super.onTouchEvent(event);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (isAnimating) {
            return super.dispatchTouchEvent(ev);
        }
        if (mGestureMode == GESTURE_MODE_DISABLED) {
            return super.dispatchTouchEvent(ev);
        }
        if (mTopView == null || mTopView.getVisibility() == GONE ||
                mContentView == null ||
                mContentView.getVisibility() != VISIBLE) {
            return super.dispatchTouchEvent(ev);
        }
        final int action = ev.getAction();
        float y = ev.getY();
        if (action == MotionEvent.ACTION_MOVE) {
            float dy = y - mLastY;
            /*
             * 如果向下滚动，有 2 种情况处理 且y在ViewPager下方
             * 1、RecyclerView 或者其它滚动的View，当mContentView滚动到顶部时，拦截事件
             * 2、非滚动控件，直接拦截事件
             */
            if (dy > 0 && isScrollTop()) {
                requestDisallowInterceptTouchEvent(false);// 父View向子View拦截分发事件
                return super.dispatchTouchEvent(ev);
            }
        }
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (isAnimating) {
            return true;
        }
        if (mGestureMode == GESTURE_MODE_DISABLED) {
            return false;
        }
        if (mTopView == null || mTopView.getVisibility() == GONE ||
                mContentView == null ||
                mContentView.getVisibility() != VISIBLE) {
            return super.onInterceptTouchEvent(ev);
        }

        final int action = ev.getAction();
        float y = ev.getY();
        float x = ev.getX();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                int index = ev.getActionIndex();
                mActivePointerId = ev.getPointerId(index);
                mLastY = downY = y;
                mLastX = x;
                if (!mHorizontalScrollEnable) {
                    mLateralSlidingEnable = false;
                    if (isTouchView(mCalendarView, ev)) {
                        mLateralSlidingEnable = true;
                        break;
                    }
                    for (View view : mHorizontalScrollList) {
                        if (isTouchView(view, ev)) {
                            mLateralSlidingEnable = true;
                            break;
                        }
                    }
                }
                break;
            case MotionEvent.ACTION_MOVE:
                float dy = y - mLastY;
                float dx = x - mLastX;
                int contentViewTranslateY = getContentViewTranslateY();
                 /*
                   如果向上滚动，且ViewPager已经收缩，不拦截事件
                 */
                float translationY = mContentView.getTranslationY();
                if (dy < 0 && translationY == -contentViewTranslateY) {
                    return false;
                }
                /*
                 * 如果向下滚动，有 2 种情况处理 且y在ViewPager下方
                 * 1、RecyclerView 或者其它滚动的View，当mContentView滚动到顶部时，拦截事件
                 * 2、非滚动控件，直接拦截事件
                 */
                boolean isScrollTop = isScrollTop();
                if (dy > 0 && y >= 0 && !isScrollTop) {
                    return false;
                }

                if (Math.abs(dy) > Math.abs(dx)) { //纵向滑动距离大于横向滑动距离,拦截滑动事件
                    if ((dy > 0 && translationY <= 0)
                            || (dy < 0 && translationY >= -contentViewTranslateY)) {
                        mLastY = y;
                        return true;
                    }
                } else if (!mHorizontalScrollEnable && !mLateralSlidingEnable && isScrollTop) {
                    return true;
                }
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }

    /**
     * 是否触控的该view
     *
     * @param view  view
     * @param event 触控事件
     * @return
     */
    private boolean isTouchView(View view, MotionEvent event) {
        if (view != null && event != null) {
            int[] location = new int[2];
            view.getLocationOnScreen(location);
            float x = event.getRawX();
            float y = event.getRawY();
            int left = location[0];
            int right = left + view.getWidth();
            int top = location[1];
            int bottom = top + view.getHeight();
            return x >= left && x <= right && y >= top && y <= bottom;
        }
        return false;
    }


    private int getPointerIndex(MotionEvent ev, int id) {
        int activePointerIndex = ev.findPointerIndex(id);
        if (activePointerIndex == -1) {
            mActivePointerId = INVALID_POINTER;
        }
        return activePointerIndex;
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        mHorizontalScrollList.clear();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (mContentView == null || mTopView == null) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            return;
        }

        int height = MeasureSpec.getSize(heightMeasureSpec);

        int topViewHeight = mTopView.getHeight();
        if (topViewHeight > 0 && topViewHeight != mContentViewTranslateY) {
            int oldContentViewTranslateY = mContentViewTranslateY;
            mContentViewTranslateY = topViewHeight;
            viewTranslateY(oldContentViewTranslateY);
        }
        if (topViewHeight >= height && topViewHeight > 0) {
            height = topViewHeight;
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(topViewHeight, MeasureSpec.EXACTLY);
        } else if (topViewHeight < height && topViewHeight > 0) {
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
        }

        int h;
        if (mTopView.getVisibility() == GONE) {
            h = height;
        } else if (mGestureMode == GESTURE_MODE_DISABLED) {
            h = height - mContentViewTranslateY;
        } else {
            h = height - mCeilingHeight;
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int heightSpec = MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY);
        mContentView.measure(widthMeasureSpec, heightSpec);
        mContentView.layout(mContentView.getLeft(), mContentView.getTop(), mContentView.getRight(), mContentView.getBottom());
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        int childCount = getChildCount();
        if (childCount <= 0) return;
        mTopView = (ViewGroup) getChildAt(0);
        mContentView = findViewById(mContentViewId);
        mCalendarView = findViewById(mCalendarViewId);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        if (mTopView != null && mContentViewTranslateY == 0) {
            mTopView.post(() -> {
                mContentViewTranslateY = mTopView.getHeight();
                if (mGestureMode == GESTURE_MODE_DISABLED) {
                    requestLayout();
                }
            });
        }
    }

    /**
     * 获取 ContentView 可滑动的高度
     *
     * @return
     */
    private int getContentViewTranslateY() {
        int translateY = mContentViewTranslateY - mCeilingHeight;
        if (translateY < 0) translateY = 0;
        return translateY;
    }

    /**
     * 平移ViewPager月视图
     */
    private void translationViewPager() {
        if (mTopView == null || mContentView == null) return;
        mTopView.setTranslationY(mContentView.getTranslationY());
    }


    @Nullable
    @Override
    protected Parcelable onSaveInstanceState() {
        Bundle bundle = new Bundle();
        Parcelable parcelable = super.onSaveInstanceState();
        bundle.putParcelable("super", parcelable);
        bundle.putBoolean("isExpand", isExpand());
        return bundle;
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        Bundle bundle = (Bundle) state;
        Parcelable superData = bundle.getParcelable("super");
        boolean isExpand = bundle.getBoolean("isExpand");
        if (isExpand) {
            post(() -> expand(0));
        } else {
            post(() -> shrink(0));

        }
        super.onRestoreInstanceState(superData);
    }

    /**
     * 滑动展开
     *
     * @param distance 滑动距离
     * @return
     */
    public boolean scrollExpand(int distance) {
        if (isAnimating || mTopView == null)
            return false;
        float viewTranslationY = mTopView.getTranslationY();
        if (viewTranslationY == -getContentViewTranslateY()) {
            if (mTopView.getVisibility() != VISIBLE) {
                mTopView.setVisibility(VISIBLE);
            }
        }
        float translationY = Math.min(Math.abs(viewTranslationY), distance);
        float endY = viewTranslationY + translationY;
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mTopView,
                "translationY", viewTranslationY, endY);
        long duration = (long) (distance);
        if (duration >= 300L) {
            duration = 300L;
        } else if (duration <= 100L) {
            duration = 100L;
        }
        objectAnimator.setDuration(duration);
        objectAnimator.setInterpolator(new DecelerateInterpolator());
        objectAnimator.addUpdateListener(new AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float currentValue = (Float) animation.getAnimatedValue();
                mTopView.setTranslationY(currentValue);
                mContentView.setTranslationY(currentValue);
                isAnimating = true;
            }
        });
        objectAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                isAnimating = false;
            }
        });
        objectAnimator.start();
        return true;
    }


    /**
     * 滑动收缩
     *
     * @param distance 滑动距离
     * @return
     */
    public boolean scrollShrink(int distance) {
        if (isAnimating || mTopView == null) {
            return false;
        }
        float viewTranslationY = mTopView.getTranslationY();
        float diff = getContentViewTranslateY() - Math.abs(viewTranslationY);
        float translationY = Math.min(diff, distance);
        float endY = viewTranslationY - translationY;
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mTopView,
                "translationY", viewTranslationY, endY);
        long duration = (long) (distance);
        if (duration >= 300L) {
            duration = 300L;
        } else if (duration <= 100L) {
            duration = 100L;
        }
        objectAnimator.setDuration(duration);
        objectAnimator.setInterpolator(new DecelerateInterpolator());
        objectAnimator.addUpdateListener(new AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float currentValue = (Float) animation.getAnimatedValue();
                mTopView.setTranslationY(currentValue);
                mContentView.setTranslationY(currentValue);
                isAnimating = true;
            }
        });
        objectAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                isAnimating = false;
                if ((int) endY == -mContentViewTranslateY) {
                    mTopView.setVisibility(INVISIBLE);
                }
            }
        });
        objectAnimator.start();
        return true;
    }

    /**
     * 是否展开了
     *
     * @return isExpand
     */
    public final boolean isExpand() {
        return mTopView.getVisibility() == VISIBLE;
    }

    public boolean expand() {
        return expand(240);
    }

    /**
     * 展开
     *
     * @param duration 时长
     * @return 展开是否成功
     */
    public boolean expand(int duration) {
        if (isAnimating || mTopView == null)
            return false;
        if (mTopView.getVisibility() != VISIBLE) {
            mTopView.setVisibility(VISIBLE);
        }
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mTopView,
                "translationY", mTopView.getTranslationY(), 0f);
        objectAnimator.setDuration(duration);
        objectAnimator.addUpdateListener(new AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float currentValue = (Float) animation.getAnimatedValue();
                mTopView.setTranslationY(currentValue);
                mContentView.setTranslationY(currentValue);
                isAnimating = true;
            }
        });
        objectAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                isAnimating = false;
                if (mGestureMode == GESTURE_MODE_DISABLED) {
                    requestLayout();
                }
                mTopView.setVisibility(VISIBLE);
            }
        });
        objectAnimator.start();
        return true;
    }

    public boolean shrink() {
        return shrink(240);
    }

    /**
     * 收缩
     *
     * @param duration 时长
     * @return 成功或者失败
     */
    public boolean shrink(int duration) {
        if (mGestureMode == GESTURE_MODE_DISABLED) {
            requestLayout();
        }
        if (isAnimating || mTopView == null) {
            return false;
        }
        int endY = getContentViewTranslateY();
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mTopView,
                "translationY", mTopView.getTranslationY(), -endY);
        objectAnimator.setDuration(duration);
        objectAnimator.addUpdateListener(new AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float currentValue = (Float) animation.getAnimatedValue();
                mTopView.setTranslationY(currentValue);
                mContentView.setTranslationY(currentValue);
                isAnimating = true;
            }
        });
        objectAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                isAnimating = false;
                if (endY == mContentViewTranslateY) {
                    mTopView.setVisibility(INVISIBLE);
                }
            }
        });
        objectAnimator.start();
        return true;
    }

    /**
     * ContentView是否滚动到顶部 如果完全不适合，就复写这个方法
     *
     * @return 是否滚动到顶部
     */
    protected boolean isScrollTop() {
        if (mContentView instanceof CalendarScrollView) {
            return ((CalendarScrollView) mContentView).isScrollToTop();
        }
        if (mContentView instanceof RecyclerView)
            return ((RecyclerView) mContentView).computeVerticalScrollOffset() == 0;
        if (mContentView instanceof AbsListView) {
            boolean result = false;
            AbsListView listView = (AbsListView) mContentView;
            if (listView.getFirstVisiblePosition() == 0) {
                final View topChildView = listView.getChildAt(0);
                result = topChildView.getTop() == 0;
            }
            return result;
        }
        return mContentView.getScrollY() == 0;
    }

    /**
     * 设置吸顶高度
     *
     * @param height 高度（px）
     */
    public void setCeilingHeight(int height) {
        this.mCeilingHeight = height;
        requestLayout();
    }

    /**
     * 设置是否支持横向滑动，默认 false 不支持
     *
     * @param enable ture 支持，false 不支持
     */
    public void setHorizontalScrollEnable(boolean enable) {
        this.mHorizontalScrollEnable = enable;
    }

    /**
     * 添加支持横向滑动的view
     *
     * @param view view
     * @return
     */
    public boolean addHorizontalScrollView(View view) {
        return this.mHorizontalScrollList.add(view);
    }

    /**
     * 移除支持横向滑动的view
     *
     * @param view view
     * @return
     */
    public boolean removeHorizontalScrollView(View view) {
        return this.mHorizontalScrollList.remove(view);
    }

    /**
     * 如果有十分特别的ContentView，可以自定义实现这个接口
     */
    public interface CalendarScrollView {
        /**
         * 是否滚动到顶部
         *
         * @return 是否滚动到顶部
         */
        boolean isScrollToTop();
    }
}
